﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.ObjectModel;

namespace EmbeddedLanguage.SubProcessors
{
    public class UpToJSProcessor : ExpressionToJSProcessor
    {

        private bool Entered;

        private string RangeVarName;

        //protected override Expression VisitListInit(ListInitExpression node)
        //{
        //    return base.VisitListInit(node);
        //}

        //protected override Expression VisitMemberInit(MemberInitExpression node)
        //{
        //    return base.VisitMemberInit(node);
        //}

        protected virtual void AddUpToArgsToNestedString(NestedString ns, ReadOnlyCollection<Expression> node_Arguments)
        {
            var current = this.CurrentNestedString;
            this.CurrentNestedString = ns;
            int i = 0;
            this.Visit(node_Arguments[0]);
            this.Visit(node_Arguments[1]);
            var rangeProcessor = new RangeJSProcessor
            {
                RangeVarName = this.RangeVarName,
            };
            this.TransferState(rangeProcessor);
            rangeProcessor.Visit(node_Arguments[2]);
            this.CurrentNestedString = current;
        }


        protected override Expression VisitMethodOrDelegateCall(Expression node, Expression node_Object,
            MemberInfo node_Method, ReadOnlyCollection<Expression> node_Arguments, bool FromInvocation)
        {
            if (Entered)
            {
                return base.VisitMethodOrDelegateCall(node, node_Object, node_Method, node_Arguments, FromInvocation);
            }
            bool FirstTimeEntering = true;
            Entered = true;
            var ns = new NestedString
            {
                Parent = this.CurrentNestedString,
                Comment = "UpToJSProcessor",
            };
            this.RemoveReturnFromParent(ns);
            if (node_Object == null)
            {
                this.RangeVarName = this.CreateNewVariableName();
                ns.TrainOfMethodsParameter = this.RangeVarName;
                if (ns.Parent.IsKaboose)
                {
                    if (ns.Parent.ReturningParent.LHS == "return ")
                    {
                        ns.Parent.ReturningParent.LHS = null;
                    }
                    
                }
                ns.IsKaboose = true;
                ns.LHS_Template = "var {RangeVarName} = { Start : {Start}, End : {End} };";
                ns.RHS_Template = "{Loop}";
                ns.Footer = @"return {RangeVarName};";
                ns.ForceLineBreakOnFooter = true;
                //ns.ForceLineBreakOnEachChild = true;
                ns.ForceLineBreakOnRHS = true;
                ns.LHS_Template = ns.LHS_Template.Replace("{RangeVarName}", this.RangeVarName);
                ns.Footer = ns.Footer.Replace("{RangeVarName}", this.RangeVarName);
                ns.ChildSubstitutions = new Dictionary<string, int>
                {
                    {"{Start}", 0},
                    {"{End}", 1},
                    {"{Loop}", 2},
                };
                //Func<int, ExpressionToJSProcessor> processors = i =>
                //{
                //    switch (i)
                //    {
                //        case 2:
                //        case 3:
                //        case 4:
                //            var extractor = new JSFunctionExtractor
                //            {
                //                KeyVariableName = this.RangeVarName,
                //            };
                //            return extractor;
                //    };
                //    return null;
                //};
                if (FirstTimeEntering)
                {
                    ns.ForceLineBreakOnEachChild = true;
                    this.AddUpToArgsToNestedString(ns, node_Arguments);
                }
                else
                {
                    this.AddArgsToNestedString(ns, node_Arguments);
                }
            }
            else
            {
                throw new NotImplementedException();
                //this.CurrentNestedString = ns;
                //this.Visit(node_Object);
                //var nsMethodCall = new NestedString
                //{
                //    Parent = ns,
                //    LHS = "." + node_Method.Name + "(",
                //    RHS = ")",
                //};
                //this.AddArgsToNestedString(nsMethodCall, node_Arguments);
            }
            this.CurrentNestedString = ns.Parent;
            return node;


        }
    }
}
